#ifndef _DXYREFLECTCLASS_H_
#define _DXYREFLECTCLASS_H_

#include <map>
#include <string>
#include <functional>
#include <memory>
#include <exception>
#include <cstring>

namespace dxy::reflect
{

class DxyRelfectObject;

class DxyReflectClass
{
    friend DxyRelfectObject;
public:
    static DxyReflectClass & getInstance();

    template<class T>
    void registerClassByName(const std::string & className, std::function<T * (void)> && f);
    std::shared_ptr<DxyRelfectObject> getClassByName(const std::string & className);
    void registerClassFieldByName(const std::string & className, const std::string & fieldName, size_t offset);
    void registerClassFunctionByName(const std::string & className, const std::string & funcName, size_t ptr);
    bool isExistClass(const std::string & className);
    bool isExistFunction(const std::string & className, const std::string & funcName);
    bool isExistField(const std::string & className, const std::string & fieldName);

private:
    DxyReflectClass();
    ~DxyReflectClass();


private:
    std::map<std::string, std::function<DxyRelfectObject * (void)>>         m_classMap;
    std::map<std::string, std::map<std::string, size_t>>                    m_classFieldMap;
    std::map<std::string, std::map<std::string, size_t>>                    m_classFunctionMap;
};

template<class T>
void DxyReflectClass::registerClassByName(const std::string & className, std::function<T * (void)> && f)
{
    m_classMap[className] = std::forward<std::function<T * (void)>>(f);
}


class DxyRelfectObject
{
public:
    virtual ~DxyRelfectObject();

    void call(const std::string & className, const std::string & funcName);
    template<class T>
    void set(const std::string & className, const std::string & fieldName, const T & src);
    template<class T>
    void get(const std::string & className, const std::string & fieldName, T & src);
};

template<class T>
void DxyRelfectObject::set(const std::string & className, const std::string & fieldName, const T & src)
{
    auto it1 = DxyReflectClass::getInstance().m_classFieldMap.find(className);
    if(it1 == DxyReflectClass::getInstance().m_classFieldMap.end()) throw std::exception();
    auto it2 = DxyReflectClass::getInstance().m_classFieldMap[className].find(fieldName);
    if(it2 == DxyReflectClass::getInstance().m_classFieldMap[className].end()) throw std::exception();
    size_t p = (size_t)this + it2->second;
    *((T*)p) = src;
}

template<class T>
void DxyRelfectObject::get(const std::string & className, const std::string & fieldName, T & src)
{
    auto it1 = DxyReflectClass::getInstance().m_classFieldMap.find(className);
    if(it1 == DxyReflectClass::getInstance().m_classFieldMap.end()) throw std::exception();
    auto it2 = DxyReflectClass::getInstance().m_classFieldMap[className].find(fieldName);
    if(it2 == DxyReflectClass::getInstance().m_classFieldMap[className].end()) throw std::exception();   
    size_t p = (size_t)this + it2->second;
    src = *((T*)p);
}


template<class T>
class DxyClassRegister
{
public:
    DxyClassRegister(const std::string & className, std::function<T * (void)> && f)
    {
        DxyReflectClass::getInstance().registerClassByName(className, std::forward<std::function<T * (void)>>(f));   
    }
};

class DxyFieldRegister
{
public:
    DxyFieldRegister(const std::string & className, const std::string & fieldName, size_t offset)
    {
        DxyReflectClass::getInstance().registerClassFieldByName(className, fieldName, offset);   
    }
};

class DxyFunctionRegister
{
public:
    DxyFunctionRegister(const std::string & className, const std::string & funcName, size_t ptr)
    {
        DxyReflectClass::getInstance().registerClassFunctionByName(className, funcName, ptr);
    }
};

#define CLASSREGISTER(className) \
    DxyClassRegister<className> dxyClassRegister##className(#className, [] () {return new className();}); \

#define CLASSFIELDREGISTER(className, fieldName) \
    className tmp##className##fieldName; \
    DxyFieldRegister dxyFieldRegister##className##fieldName(#className, #fieldName, ((size_t)&tmp##className##fieldName.fieldName - (size_t)&tmp##className##fieldName)); \

#define CLASSFUNCREGISTER(className, funcName) \
    std::function<void (className*)> tmpFunc##className##funcName = &className::funcName; \
    DxyFunctionRegister dxyFunctionRegister##className##funcName(#className, #funcName, (size_t)&tmpFunc##className##funcName);

}


#endif